package com.warom.sdg.config;

import com.warom.sdg.enums.ModuleType;
import com.warom.sdg.model.DeviceType;
import com.warom.sdg.model.PlcDataEntity;
import com.warom.sdg.util.ModbusHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * PLC测点工具类
 * 提供测点查询、验证等功能
 */
public class PlcMeasurementUtils {
    
    /**
     * 根据英文名称获取测点
     * 
     * @param englishName 测点英文名称
     * @return 测点对象，如果未找到则返回null
     */
    public static MeasurementPoint getPointByEnglishName(String englishName) {
        return PlcMeasurementConfig.englishNamePointMap.get(englishName);
    }

    /**
     * 处理属性测点数据，更新对应的主测点
     * 
     * @param englishName 属性测点英文名称
     * @param value       属性测点值
     * @return 是否成功处理
     */
    public static boolean processAttributePoint(String englishName, double value) {
        // 直接通过英文名称查找属性测点
        MeasurementPoint attributePoint = getPointByEnglishName(englishName);
        if (attributePoint == null || !attributePoint.isAttributePoint()) {
            return false; // 不是属性测点或未找到测点
        }

        // 设置属性测点的值，会自动处理值变化并更新主测点
        attributePoint.setValue(value);
        return true;
    }

    /**
     * 判断是否为运行状态属性测点
     * 
     * @param englishName 测点英文名称
     * @return 是否为运行状态属性测点
     */
    public static boolean isRunStatusPoint(String englishName) {
        MeasurementPoint point = getPointByEnglishName(englishName);
        return point != null && point.getPointType() == MeasurementPoint.PointType.RUN_STATUS;
    }

    /**
     * 判断是否为故障报警属性测点
     * 
     * @param englishName 测点英文名称
     * @return 是否为故障报警属性测点
     */
    public static boolean isFaultAlarmPoint(String englishName) {
        MeasurementPoint point = getPointByEnglishName(englishName);
        return point != null && point.getPointType() == MeasurementPoint.PointType.FAULT_ALARM;
    }

    /**
     * 判断是否为属性测点
     * 
     * @param englishName 测点英文名称
     * @return 是否为属性测点
     */
    public static boolean isAttributePoint(String englishName) {
        MeasurementPoint point = getPointByEnglishName(englishName);
        return point != null && point.isAttributePoint();
    }

    /**
     * 获取指定模块下指定设备的所有测点信息
     * 
     * @param moduleType 模块类型
     * @param deviceType 设备类型
     * @return 测点信息列表
     */
    public static List<MeasurementPoint> getMeasurementPoints(ModuleType moduleType, DeviceType deviceType) {
        Map<DeviceType, List<MeasurementPoint>> devicePointsMap = PlcMeasurementConfig.moduleDevicePointsMap.get(moduleType);
        if (devicePointsMap == null) {
            return new ArrayList<>();
        }

        List<MeasurementPoint> points = devicePointsMap.get(deviceType);
        return points != null ? points : new ArrayList<>();
    }

    /**
     * 根据设备ID获取指定模块的测点信息
     * 
     * @param moduleType   模块类型
     * @param deviceTypeId 设备类型ID
     * @return 测点信息列表
     */
    public static List<MeasurementPoint> getMeasurementPointsByDeviceId(ModuleType moduleType, int deviceTypeId) {
        DeviceType deviceType = DeviceType.getById(deviceTypeId);
        if (deviceType == null) {
            return new ArrayList<>();
        }
        return getMeasurementPoints(moduleType, deviceType);
    }

    /**
     * 获取指定模块的所有测点，按设备类型分组
     * 
     * @param moduleType 模块类型
     * @return 按设备类型分组的测点信息
     */
    public static Map<DeviceType, List<MeasurementPoint>> getAllMeasurementPointsByModule(ModuleType moduleType) {
        return PlcMeasurementConfig.moduleDevicePointsMap.getOrDefault(moduleType, new HashMap<>());
    }

    /**
     * 将指定设备的测点信息转换为PLC数据实体列表
     * 
     * @param deviceId   设备ID（数据库中的ID）
     * @param deviceType 设备类型
     * @param moduleType 模块类型
     * @return PLC数据实体列表
     */
    public static List<PlcDataEntity> getMeasurementEntities(int deviceId, DeviceType deviceType,
                ModuleType moduleType) {
        List<MeasurementPoint> points = getMeasurementPoints(moduleType, deviceType);
        List<PlcDataEntity> entities = new ArrayList<>();

        for (MeasurementPoint point : points) {
            entities.add(point.toPlcDataEntity(deviceId));
        }

        return entities;
    }

    /**
     * 根据设备类型ID将测点信息转换为PLC数据实体列表
     * 
     * @param deviceId     设备ID（数据库中的ID）
     * @param deviceTypeId 设备类型ID
     * @param moduleType   模块类型
     * @return PLC数据实体列表
     */
    public static List<PlcDataEntity> getMeasurementEntitiesByDeviceTypeId(int deviceId, int deviceTypeId,
                ModuleType moduleType) {
        DeviceType deviceType = DeviceType.getById(deviceTypeId);
        if (deviceType == null) {
            return new ArrayList<>();
        }
        return getMeasurementEntities(deviceId, deviceType, moduleType);
    }

    /**
     * 添加新的测点配置
     * 
     * @param moduleType 模块类型
     * @param deviceType 设备类型
     * @param point      测点信息
     */
    public static void addMeasurementPoint(ModuleType moduleType, DeviceType deviceType, MeasurementPoint point) {
        Map<DeviceType, List<MeasurementPoint>> devicePointsMap = PlcMeasurementConfig.moduleDevicePointsMap.computeIfAbsent(
                        moduleType, k -> new HashMap<>());

        List<MeasurementPoint> points = devicePointsMap.computeIfAbsent(
                        deviceType, k -> new ArrayList<>());

        points.add(point);
    }

    /**
     * 根据设备类型ID添加新的测点配置
     * 
     * @param moduleType   模块类型
     * @param deviceTypeId 设备类型ID
     * @param point        测点信息
     * @return 是否添加成功
     */
    public static boolean addMeasurementPointByDeviceTypeId(ModuleType moduleType, int deviceTypeId,
                MeasurementPoint point) {
        DeviceType deviceType = DeviceType.getById(deviceTypeId);
        if (deviceType == null) {
            return false;
        }

        addMeasurementPoint(moduleType, deviceType, point);
        return true;
    }

    /**
     * 验证地址格式是否正确
     * 
     * @param address Modbus地址
     * @return 是否有效
     */
    public static boolean isValidAddress(String address) {
        return ModbusHelper.parseModbusAddress(address) != null;
    }

    /**
     * 获取测点对应的类型描述
     * 
     * @param address Modbus地址
     * @return 类型描述
     */
    public static String getAddressTypeDescription(String address) {
        ModbusHelper.ModbusAddressInfo info = ModbusHelper.parseModbusAddress(address);
        return info != null ? info.typeDescription : "未知类型";
    }

    /**
     * 获取指定地址的Modbus信息
     * 
     * @param address Modbus地址
     * @return Modbus地址信息，如果地址无效返回null
     */
    public static ModbusHelper.ModbusAddressInfo getAddressInfo(String address) {
        return ModbusHelper.parseModbusAddress(address);
    }

    /**
     * 获取所有支持的设备类型
     * 
     * @return 设备类型列表
     */
    public static List<DeviceType> getAllDeviceTypes() {
        return DeviceType.getAllDeviceTypes();
    }

    /**
     * 获取所有支持的模块类型
     * 
     * @return 模块类型列表
     */
    public static List<ModuleType> getAllModuleTypes() {
        return new ArrayList<>(List.of(ModuleType.values()));
    }

    /**
     * 获取指定模块下支持的设备类型
     * 
     * @param moduleType 模块类型
     * @return 支持的设备类型列表
     */
    public static List<DeviceType> getSupportedDeviceTypes(ModuleType moduleType) {
        Map<DeviceType, List<MeasurementPoint>> devicePointsMap = PlcMeasurementConfig.moduleDevicePointsMap.get(moduleType);
        if (devicePointsMap == null) {
            return new ArrayList<>();
        }

        return new ArrayList<>(devicePointsMap.keySet());
    }

    /**
     * 根据设备类型ID和地址查找测点配置
     * 
     * @param deviceTypeId 设备类型ID
     * @param address      Modbus地址
     * @return 匹配的测点配置，如果找不到返回null
     */
    public static MeasurementPoint findMeasurementPoint(int deviceTypeId, String address) {
        DeviceType deviceType = DeviceType.getById(deviceTypeId);
        if (deviceType == null || address == null) {
            return null;
        }

        // 遍历所有模块
        for (ModuleType moduleType : getAllModuleTypes()) {
            // 获取该模块下指定设备类型的所有测点
            List<MeasurementPoint> points = getMeasurementPoints(moduleType, deviceType);

            // 在测点列表中查找匹配地址的测点
            for (MeasurementPoint point : points) {
                if (address.equals(point.getAddress())) {
                    return point;
                }
            }
        }

        return null;
    }

    /**
     * 根据设备类型ID和地址查找测点配置，并转换为PLC数据实体
     * 
     * @param deviceId     设备ID（用于填充到返回的实体中）
     * @param deviceTypeId 设备类型ID
     * @param address      Modbus地址
     * @return PLC数据实体，如果找不到匹配的测点返回null
     */
    public static PlcDataEntity findMeasurementEntity(int deviceId, int deviceTypeId, String address) {
        MeasurementPoint point = findMeasurementPoint(deviceTypeId, address);
        return point != null ? point.toPlcDataEntity(deviceId) : null;
    }

    /**
     * 测点信息类，包含测点和对应的设备类型
     */
    public static class MeasurementPointInfo {
        private MeasurementPoint point;
        private DeviceType deviceType;

        public MeasurementPointInfo(MeasurementPoint point, DeviceType deviceType) {
            this.point = point;
            this.deviceType = deviceType;
        }

        public MeasurementPoint getPoint() {
            return point;
        }

        public DeviceType getDeviceType() {
            return deviceType;
        }
    }

    /**
     * 根据测点英文名称查找测点配置及其设备类型
     * 
     * @param englishName 测点英文名称
     * @return 包含测点和设备类型的信息对象，如果找不到返回null
     */
    public static MeasurementPointInfo findMeasurementPointInfoByEnglishName(String englishName) {
        if (englishName == null || englishName.isEmpty()) {
            return null;
        }

        // 遍历所有模块
        for (ModuleType moduleType : getAllModuleTypes()) {
            // 获取该模块下的所有设备类型
            Map<DeviceType, List<MeasurementPoint>> devicePointsMap = getAllMeasurementPointsByModule(moduleType);

            // 遍历所有设备类型
            for (DeviceType deviceType : devicePointsMap.keySet()) {
                // 获取该设备类型的所有测点
                List<MeasurementPoint> points = devicePointsMap.get(deviceType);

                // 在测点列表中查找匹配英文名称的测点
                for (MeasurementPoint point : points) {
                    if (englishName.equals(point.getEnglishName())) {
                        return new MeasurementPointInfo(point, deviceType);
                    }
                }
            }
        }

        return null;
    }

    /**
     * 根据测点英文名称查找测点配置
     * 
     * @param englishName 测点英文名称
     * @return 匹配的测点配置，如果找不到返回null
     */
    public static MeasurementPoint findMeasurementPointByEnglishName(String englishName) {
        // 直接从映射中获取，速度更快
        return getPointByEnglishName(englishName);
    }
}
